package com.example.customclassloader;

import org.springframework.stereotype.Component;
import sun.misc.URLClassPath;

import java.lang.reflect.Field;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

/**
 * @author xionglei
 * @version 1.0.0
 * 2021年08月18日 17:35
 */
public class ExportDelegateClassLoader extends URLClassLoader {

    private final ClassContainer container = new ClassContainer(getClass().getClassLoader(), "rocketmq-client-4.7.1.jar", "protobuf-java-3.17.3.jar");
    // 下面继续创建其他ClassContainer, 这里省略
    private final Map<String, ClassContainer> exportedClasses = new HashMap<>();

    public ExportDelegateClassLoader(ClassLoader delegate) {
        super(findUcp(delegate).getURLs(), delegate.getParent());
        if (!(delegate instanceof URLClassLoader)) {
            throw new UnsupportedOperationException("can only support URLClassLoader");
        }
        // 这里设置上需要导出的类的类名与ClassContainer的关系
        exportedClasses.put("org.apache.rocketmq.client.consumer.MQPushConsumer", container);
    }

    private static URLClassPath findUcp(ClassLoader delegate) {
        Class<?> type = delegate.getClass();
        try {
            do {
                final Field[] fields = type.getDeclaredFields();
                for (Field field : fields) {
                    if ("ucp".equals(field.getName())) {
                        field.setAccessible(true);
                        return (URLClassPath) field.get(delegate);
                    }
                }
                type = type.getSuperclass();
            } while (URLClassLoader.class.isAssignableFrom(type));
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        // 理论上不会走到这里
        return null;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (exportedClasses.containsKey(name)) {
            // 从ClassContainer中获取
            return exportedClasses.get(name).getClass(name);
        }
        // ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        return super.loadClass(name);
    }
}
